#include "driver_led.h"
#include "stdio.h"
void bsp_led_toggle(typdef_gpio_general* LEDx);
void bsp_led_on(typdef_gpio_general* LEDx);
void bsp_led_off(typdef_gpio_general* LEDx);

//LED灯注册
LED_DEF(STATUS_LED, E, 5, SET);     //状态指示灯 PE5 初始状态高
LED_DEF(BLUEBOOTH_LED, E, 7, SET);  //蓝牙指示灯 PE7 初始状态高
LED_DEF(GREEN_LED1, D, 10, SET);    //绿色LED1 PD10 初始状态高
LED_DEF(GREEN_LED2, D, 11, SET);    //绿色LED2 PD11 初始状态高
LED_DEF(RED_LED1, D, 12, SET);      //红色LED1 PD12 初始状态高
LED_DEF(RED_LED2, D, 13, SET);      //红色LED2 PD13 初始状态高
//LED灯初始化数组
const void* LED_INIT_GROUP[] = {&STATUS_LED,		//状态LED	
                                &BLUEBOOTH_LED,		//蓝牙LED
                                &GREEN_LED1,		//绿色LED1
                                &GREEN_LED2,		//绿色LED2
                                &RED_LED1,			//红色LED1
                                &RED_LED2			//红色LED2
                               };
//LED灯数组大小
const uint8_t LED_SIZE = sizeof(LED_INIT_GROUP) / sizeof(void*);

//闪烁结构体
typedef struct {
	uint16_t BlankCount; // 闪烁次数
	uint16_t BlankTime;  // 闪烁时间
	uint16_t BlankStartStatus; // 闪烁开始时的状态
} LED_Blank_s;
//LED闪烁灯类型最大值
LED_Blank_s gLED_Blank[LED_MAX];
/**
 * @brief 初始化LED
 *
 * 该函数用于初始化LED设备，使其可以正常工作
 * 它通过调用底层驱动的初始化函数来配置LED的GPIO引脚
 *
 * @param LEDx 指向LED设备的指针，该设备包含有关GPIO的配置信息
 */
void bsp_led_init(typdef_gpio_general* LEDx) {
	RT_ASSERT(LEDx != RT_NULL);
	// 调用底层驱动函数来初始化LED的GPIO配置
	driver_gpio_general_init(LEDx);
}


/**
 * @brief 初始化LED组
 *
 * 该函数遍历LED组中的所有LED，并调用单个LED的初始化函数
 * 它使用了LED_INIT_GROUP数组，该数组包含了需要初始化的LED的配置信息
 */
void bsp_led_group_init(void) {
	// 初始化LED组中每个LED的索引变量
	uint8_t i;
	// 遍历LED组中的所有LED
	for (i = 0; i < LED_SIZE; i++) {
		// 对每个LED调用初始化函数，传入其配置信息
		bsp_led_init(((typdef_gpio_general*)LED_INIT_GROUP[i]));
	}
	memset(&gLED_Blank, 0, sizeof(LED_Blank_s));
}

/**
 * @brief 打开LED。
 *
 * 该函数通过将指定LED的引脚状态设置为其默认状态的相反状态来打开LED。
 * 这里展示了为什么以及如何打开LED：通过改变连接LED的GPIO引脚的状态，
 * 通常这个状态与默认状态相反，因为LED通常是通过低电平或高电平点亮的。
 *
 * @param LEDx 指向typedef_gpio_general结构体的指针，表示要控制的LED。
 */
void bsp_led_on(typdef_gpio_general* LEDx) {
	RT_ASSERT(LEDx != RT_NULL);
	// 调用底层GPIO驱动的pin_write函数来设置LED的状态为其默认状态
	driver_gpio_pin_write(LEDx, (bit_status)!(LEDx->default_state));
}

/**
 * @brief 关闭LED灯
 *
 * 本函数通过将LED的状态重置为其默认状态来关闭LED灯。这主要是通过调用底层的GPIO驱动函数来实现的，
 * 该函数负责实际的硬件操作。此设计考虑了LED在不同应用场景下的状态控制，提供了一种统一的关闭方式。
 *
 * @param LEDx LED设备的结构体指针，包含了LED的相关配置信息，如默认状态等。
 */
void bsp_led_off(typdef_gpio_general* LEDx) {
	RT_ASSERT(LEDx != RT_NULL);
	// 将LED的状态设置为其默认状态，以关闭LED灯
	driver_gpio_pin_write(LEDx, LEDx->default_state);
}

/**
 * @brief 切换LED的状态
 *
 * 本函数通过调用底层驱动函数来切换LED的亮灭状态。当LED亮时，调用此函数将使其熄灭；
 * 当LED熄灭时，调用此函数将使其点亮。这是通过GPIO（通用输入输出）操作实现的。
 *
 * @param LEDx 指向LED的指针，这是一个封装了LED相关配置的结构体
 */
void bsp_led_toggle(typdef_gpio_general* LEDx) {
	RT_ASSERT(LEDx != RT_NULL);
	// 调用底层GPIO驱动的pin_toggle函数来切换LED的亮灭状态
	driver_gpio_pin_toggle(LEDx);
}

/**
 * @brief 控制LED的状态
 *
 * 本函数根据传入的控制命令来控制指定LED的状态。它可以点亮LED、熄灭LED或使LED状态翻转。
 *
 * @param index LED的索引，用于指定要控制的LED。
 * @param ctrl 控制命令，决定LED的状态。必须是LED_STATUS_e枚举类型的一个值。
 */
void LED_Ctrl(LED_e index, LED_STATUS_e ctrl) {
	// 确保控制命令在有效范围内
	RT_ASSERT(ctrl < LED_MAX_STATUS);
	// 确保LED索引在有效范围内
	RT_ASSERT(index < LED_MAX);

	// 获取指定LED的初始化组配置
	typdef_gpio_general* led = (typdef_gpio_general*)LED_INIT_GROUP[index];
	// 确保LED配置不为空
	RT_ASSERT(led != RT_NULL);

	// 根据控制命令执行相应的操作
	switch (ctrl) {
		case LED_ON:
			// 点亮LED
			bsp_led_on(led);
			gLED_Blank[index].BlankCount = 0;
			gLED_Blank[index].BlankStartStatus = LED_ON;
			gLED_Blank[index].BlankTime = 0;
			break;
		case LED_OFF:
			// 熄灭LED
			gLED_Blank[index].BlankCount = 0;
			gLED_Blank[index].BlankStartStatus = LED_OFF;
			gLED_Blank[index].BlankTime = 0;
			bsp_led_off(led);
			break;
		case LED_TOGGLE:
			// 翻转LED状态
			bsp_led_toggle(led);
			break;
		default:
			// 默认情况下不执行任何操作
			break;
	}
}

/**
 * 重置并切换LED状态
 * 
 * 此函数用于重置LED的空白时间，设置新的空白开始状态，并增加空白计数
 * 同时，它会切换指定索引的LED的点亮状态
 * 
 * @param led LED_Blank_s类型的指针，指向LED的空白结构体
 * @param new_status LED_STATUS_e类型，表示新的LED空白开始状态
 * @param index LED_e类型，表示LED的索引
 */
static void reset_and_toggle(LED_Blank_s* led, LED_STATUS_e new_status, LED_e index) {
    // 重置LED的空白时间为0
    led->BlankTime = 0;
    // 设置LED的空白开始状态为新的状态
    led->BlankStartStatus = new_status;
    // 增加LED的空白计数
    led->BlankCount++;
    // 切换指定索引的LED的点亮状态
    LED_Ctrl(index, LED_TOGGLE);
}

/**
 * @brief 控制LED闪烁
 * 
 * 本函数根据指定的参数使LED以一定的频率闪烁特定次数。它通过计算LED的开启和关闭时间来控制闪烁过程。
 * 
 * @param index LED标识符，用于指定哪个LED进行闪烁
 * @param OpenTime LED每次点亮的持续时间
 * @param CloseTimes LED每次熄灭的持续时间
 * @param BlinktNums LED闪烁的次数，如果设置为pBlankNumMAX则持续闪烁
 * @param BaseTimes 基准倍数，用于计算实际的开启和关闭时间
 */
void Led_Blinking(LED_e index,
                  uint16_t OpenTime,
                  uint16_t CloseTimes,
                  uint16_t BlinktNums,
                  uint8_t BaseTimes) {
    // 获取指定LED的控制结构体指针
	LED_Blank_s *led = &gLED_Blank[index];
	
    // 确保LED标识符在有效范围内
	RT_ASSERT(index < LED_MAX);
    // 确保BaseTimes不为0，避免除以0的错误
	RT_ASSERT(BaseTimes != 0);
	
	// TODO: 实现LED闪烁逻辑
    // 如果闪烁次数达到最大值，则重置并切换LED状态
	if (BlinktNums == pBlankNumMAX) {
		reset_and_toggle(led, LED_OFF, index);
		return;
	}
	
    // 增加LED的计时器值
	led->BlankTime++;
	
    // 如果LED初始状态为开启，则检查是否达到关闭时间
	if (led->BlankStartStatus == LED_ON) {
		if (led->BlankTime >= OpenTime / BaseTimes) {
			reset_and_toggle(led, LED_OFF, index);
		}
	} else {
        // 如果LED初始状态为关闭，则检查是否达到开启时间
		if (led->BlankTime >= CloseTimes / BaseTimes) {
			reset_and_toggle(led, LED_ON, index);
		}
	}
	
    // 检查是否完成所有闪烁次数，如果是，则重置计数器并关闭LED
	if (led->BlankCount >= BlinktNums * 2) {
		led->BlankCount = 0;
		LED_Ctrl(index, LED_OFF);
	}
}





